home *** CD-ROM | disk | FTP | other *** search
/ Shareware Super Platinum 8 / Shareware Super Platinum 8.iso / mac / PROGTOOL / MPI-1.ZIP;1 / MULTP.DOC < prev    next >
Encoding:
Text File  |  1989-11-13  |  66.3 KB  |  1,214 lines

  1. { MPI - Multiple Precision Positive Integer Arithmetic Package   }
  2. { Copyright 1989, KSU Research Foundation, Manhattan KS  66506   }
  3. { License granted to copy, but not for sale or profit   }
  4.          M U L T I P L E   P R E C I S I O N   P O S I T I V E
  5.                   I N T E G E R   A R I T H M E T I C
  6.                            by Kenneth Conrow
  7.                            CTA, Cardwell Hall
  8.                         Kansas State University
  9.                           Manhattan, KS  66506
  10.  
  11.  
  12. This is a multiple precision positive integer (MPI) arithmetic package.
  13. The two versions supplied handle integers up to 149 or up to 3029
  14. decimal digits.  The package is written in Eric Isaacson's A86 assembler
  15. for the 8086 series of processors.  It runs on the 8086, 80286, and
  16. 80386 processors and uses word oriented processing, i.e. employs integer
  17. word (16-bit at a time) arithmetic.  While it runs on the 80286 and
  18. 80386 processors, it uses none of the enhanced commands available on
  19. those processors, but is written to the common denominator represented
  20. by the 8086 chip.  No use is made of 8087 coprocessor commands, and no
  21. advantage will accrue to owners of a coprocessor in their use of these
  22. routines.
  23.  
  24. The arithmetic operations supported herein are addition, subtraction,
  25. multiplication, division, left shifting (by 1 to 15 bits), and
  26. probabilistic primality testing.  To allow input and output of large
  27. numeric values for the package, two conversion routines are provided.
  28. For input, COND2B converts a decimal numeric string into the internal
  29. binary notation the package uses.  For output, CONB2D converts an
  30. internal binary number into a decimal numeric string which can be output.
  31.  
  32. In addition to the assembler routines which are the core of this
  33. contribution, there are also included a number of Turbo Pascal programs
  34. which exercise the MPI routines.  These are of some interest in their
  35. own right.  Most computer owners will be impressed with the output of
  36. MERSENNE, which forms and prints the value of the first few Mersenne
  37. numbers.  Only persons with rather specialized interests will be fond of
  38. prime number generation (P50TO128), or factoring (CFFFCOMB).  But the
  39. main point of the example routines is to illustrate for potential users
  40. some of the capabilities and techniques for use of high precision
  41. positive integer arithmetic.
  42.  
  43. The package is provided in two forms:  as the source code and as the
  44. assembled object code.  Most users should find that the object code can
  45. be linked with object code from other sources for use without
  46. modification and that possession of the A86 assembler is not required.
  47. If, for some reason, reassembly is required, the A86 assembler is widely
  48. available from shareware distribution libraries and bulletin boards.
  49. Alternatively, the A86 source code could be revised to meet the
  50. requirements of a different assembler.
  51.  
  52. In addition to the functional routines, there is also included a Turbo
  53. Pascal interface which provides a mechanism by which Turbo Pascal 4 may
  54. be employed to handle your algorithm.  The details of linkage to the
  55. invoked MPI routines are handled by the interface.  The flavor of a
  56. Pascal routine using this package is rather assembler-level, because you
  57. have to define a series of registers and keep track of them yourself in
  58. order to successfully use the package.  The details of keeping values in
  59. registers out of the way of subsequent register reuse by the MPI package
  60. sometimes entail rather low level considerations which must be met from
  61.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 2
  62.  
  63. Turbo Pascal.  Thus, the interface does provide the programmer with
  64. nominal high level language access to multiple precision arithmetic, but
  65. that access remains somewhat awkward and detailed.
  66.  
  67. While it is true that there is no requirement for ownership of A86 or
  68. D86 to employ this multiple precision positive integer arithmetic
  69. package, it is also true that there is a very strong need for some kind
  70. of debugger (e.g.  D86, a good one) in order to successfully program and
  71. debug programs applying it.  Opportunities for error are so rife and the
  72. territory covered in a typical application is so large that it would be
  73. very difficult to work effectively without a debugger.
  74.  
  75.  
  76. --Languages available for multiple precision arithmetic
  77.  
  78. This package was written to do large volume large positive integer
  79. arithmetic, especially primality testing, on the microcomputer.  One
  80. wants to have a program which runs as efficiently as possible, hence the
  81. choice of an assembler language.
  82.  
  83. REXX is available for PCs and does support multiple precision
  84. arithmetic, but I have not used it.  SAVVY does 64 digit mixed number
  85. arithmetic (which allows primality testing to about 31 decimal digits)
  86. and I used it extensively prior to development of this package.  The
  87. name (SAVVY.SAM) of the sample input file for PPTEST derives from this
  88. historical fact.  This package is many fold (about 54) faster than SAVVY
  89. in primality testing on my particular 8086 machine.
  90.  
  91. Available multiple precision arithmetic programs/systems on IBM
  92. mainframes are either interpreted (hence relatively slow, e.g. REXX),
  93. or designed for floating point numbers (e.g.  Ehrman's Multiple
  94. Precision Floating Point Arithmetic, SPLA 360D-40.4.003 and
  95. Communications of the ACM, 11, 517-520, July 1968).
  96.  
  97. The trouble with using mixed/real number arithmetic routines for integer
  98. arithmetic is that determining the integer remainder from a division is
  99. a multiple step process -- divide, separate the fractional part, and
  100. multiply it by the divisor.  This is wasteful since integer division
  101. could more efficiently determine the remainder with even less work than
  102. dividing out the fractional part in full, let alone the extra
  103. manipulations of fractional part separation and multiplication.  This
  104. mixed number disadvantage is shared by SAVVY and Ehrman's multiple
  105. precision arithmetic package.  In addition, keeping a floating point
  106. value normalized and alignment of the operands for addition and
  107. subtraction by shifting of the mantissa when required (as is done in
  108. Ehrman's package) represents unnecessary overhead when the work is known
  109. to involve integer values.  The internals of SAVVY are hidden so one
  110. can't say what internal manipulations limit its speed.
  111.  
  112.  
  113. --Algorithms and conventions employed herein
  114.  
  115. This package employs the 'classical' multiple precision arithmetic
  116. algorithms provided by Donald E. Knuth, The Art of Computer Programming,
  117. Vol.  II, Seminumerical Algorithms, Addison-Wesley, Second Edition
  118. (1981), pp250ff.  Although asymptotically better algorithms sometimes
  119. exist, it seemed unlikely in the range of precision I have been using
  120. (up to 29 digits, decimal) that they would provide enough extra
  121. efficiency to justify their implementation.  Interestingly, Knuth (p260)
  122. estimates that division using the classical algorithm is only about 7%
  123.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 3
  124.  
  125. more time consuming than multiplication.  The exponentiation entailed in
  126. the probabilistic primality test and in POWERMOD in CFFFCOMB is done by
  127. Algorithm A, p442, Knuth, idem.
  128.  
  129. Numbers are represented in this package in radix 65536 notation.  The
  130. words appear in the usual order, with coefficients of decreasing powers
  131. of 65536 appearing from left to right.  Thus, multiple-word values are
  132. simply interpretable as large binary or hexadecimal numbers.  Every
  133. number (except the result of zero divide) bears a leading zero word as a
  134. guard word, and the offsets used to indicate operands to the routines
  135. always point to this guard word.  In addition to the offsets, a length
  136. value (in words) is maintained for every numeric value.  Thus, both an
  137. offset value and a length value are typically passed to a routine to
  138. specify an operand to it, and a routine typically returns a result as an
  139. (offset,length) pair.  When a subtraction or division operation results
  140. in reduction of the number of significant words, the offset and length
  141. values are adjusted to update them (without shifting the significant
  142. words) before the result descriptor is returned.
  143.  
  144. The use of a guard word whose value is zero in front of the actual value
  145. of each number was convenient for several reasons.  It is consistent
  146. with the use of zero-origin indexing so natural in assembler level
  147. programming.  The guard word provides a place for expansion in the event
  148. that addition or leftshifting results in a carry into an additional
  149. (radix 65536) digit.  The q(0) digit may be filled by Knuth's division
  150. algorithm, so it is handy to have it present.
  151.  
  152. The addition, subtraction, left shifting, and division routines actively
  153. replace any values in the guard words of their arguments with zero.
  154. The results created by all routines bear a leading zero guard word.  If
  155. a user generates his own values for use as arguments to these routines,
  156. he should take care to provide and zero this leading guard word, for
  157. safety's sake, even though the package seems to be fairly tolerant if
  158. the guard word is sometimes non-zero in the arguments passed it.
  159.  
  160. The number zero is represented by two consecutive words with value of
  161. zero.  The first is the zero-valued guard word and the second is the
  162. actual zero which is represented.  (O.K., quibbler, this is really a
  163. multiple-precision non-negative integer arithmetic package.)
  164.  
  165. This package does its work in a series of registers (which are simply
  166. word sequences reserved for the specific purpose of holding one or
  167. another of the values to be manipulated).  The user will need to provide
  168. as many registers as are required to hold the set of values employed by
  169. his algorithm.  (MPI has some internal registers of its own, too).  As
  170. supplied in MPIFACE.OBJ, MULTP.OBJ, and PROBP.OBJ the registers are 32
  171. words long, which allows for 149 digit decimal numbers.  There is no
  172. general need for the user to use such long registers for his own
  173. variables if he is certain that his values are always shorter.
  174. (DIVTEST, for example, uses a smaller value for the register length.)
  175. However, the space provided to COND2B for it to place the binary
  176. equivalent of its decimal string argument must be 32 (or 630 for the
  177. long version) words long, because COND2B assumes this length when it
  178. calculates the position of the right edge of a register at which to
  179. align the resulting integer.  The variant supplied in MPIFACEL.OBJ has a
  180. register length of 630 words, enough to hold about a 3029 digit decimal
  181. number.
  182.  
  183.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 4
  184.  
  185. Changing the value of RLEN in the RLEN EQU statement in the source code
  186. from 32 to another number will result in the reservation of a different
  187. number of words for each internal register employed by the package so
  188. that longer integers can be processed or space can be saved.  If this is
  189. done, then the code will need to be re-assembled using A86; normally,
  190. when 31 words (149 decimal digits) of precision (1 word goes to the
  191. guard word) is adequate, there should be no need to reassemble the code.
  192. The use of such long lengths for the internal registers may seem
  193. wasteful, but since there are only 5 such registers (really 4, but one
  194. is double length), this amounts to only 320 bytes for all the internal
  195. registers in the short versions (3150 bytes in the long version) so the
  196. space to be conserved by reassembly is minimal.
  197.  
  198. Any user-defined registers should be word aligned for maintenance of
  199. efficiency of operand fetching.  A measure of the importance of this
  200. consideration was gained in timing runs on LUCLEHMR, q.v. below, where a
  201. penalty of 15% was observed when a heavily used register was
  202. deliberately mis-aligned to a byte boundary on an 80386 based machine.
  203.  
  204. (For the curious, MPIPR, MPIQO, MPIXP, and MPIY are the names of the
  205. internal registers.  The MPI prefix denotes "Multiple-precision Positive
  206. Integer".  The variable names employed in the source for this package
  207. are intended to be peculiar enough that they will not interfere with
  208. variable names selected by a user wishing to include these routines as
  209. service routines in his own assembler source code (as is done when 'A86
  210. PPTEST.8 MULTP.8 PROBP.8' is issued, see below)).
  211.  
  212.  
  213. --Details about routines
  214.  
  215. A user intending to access the multiple precision routines from Turbo
  216. Pascal 4 can probably just skim this description of the internal
  217. details of this package to get a general impression of the function of
  218. each of the MPI routines and its limitations, before concentrating on
  219. reading about the use of the Pascal interface module.
  220.  
  221. The conventions employed herein are quite idiosyncratic, and not at all
  222. consistent with conventional practice among microcomputer assembler
  223. programmers.  The routines regularly destroy the contents of the data,
  224. pointer, and index registers, AX,BX,CX,DX,BP,SI,DI, but do not alter the
  225. values in the segment registers.  The SP value is modified implicitly by
  226. the pushing and popping activity performed in handling the arguments and
  227. returning the values via the stack.  (This method of handling arguments
  228. and returned values was learned from an exposure to FORTH.)  Local
  229. variables are mostly kept either in the code segment or the data segment;
  230. almost none are housed on the stack.  Since register contents are
  231. routinely destroyed by these routines, the user must keep local
  232. variables around the routine invocations by some mechanism other than
  233. residence in a register.  This is done in these routines whenever one of
  234. them calls another, lower-level, routine by use of local variables in the
  235. code or data segment.
  236.  
  237.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 5
  238.  
  239. All arguments to the routines are passed on the stack; most returned
  240. values are returned on the stack, except for the primality testing
  241. routine which returns its result in the AL register.  Most numeric
  242. values are passed by reference via an offset value on the stack; offsets
  243. of numeric values actually point to the leading guard word (whose value
  244. is conventionally zero).  The following table gives the routine names,
  245. the arguments to be passed, and the results returned.  The caret in
  246. front of the value component of most arguments means the offset of the
  247. value is passed; the actual lengths (not offsets of the lengths), in
  248. words, are stacked; for SHIFTLEFT, the value (not the offset of a value)
  249. on the stack specifies the number of bits of the left shift.
  250.  
  251.  
  252.          ASSEMBLER LEVEL SUBROUTINE ARGUMENT AND RESULT SUMMARY
  253.  
  254. routine
  255. name      argument list in stack              result list in stack
  256. _______   __________________________          ________________________
  257.  
  258. ADDER     ^ad1,length(ad1),^ad2,length(ad2)   ^sum,length(sum)
  259. CONB2D    ^right(decstr),^op,length(op)       ^left(decstr),length(decstr)
  260. COND2B    ^left(register),^left(decstr)       ^result,length(result)
  261. DIVIDE    ^dvd,length(dvd),^dsr,length(dsr)   ^quot,length(quot),^rem,length(rem)
  262. MULTIPLY  ^mp1,length(mp1),^mp2,length(mp2)   ^prod,length(prod)
  263. PROBPRIM  ^op,length(op)                      {'P' or 'C' in AL}
  264. SHIFTLEFT ^op1,length(op1),2**n               ^result,length(result)
  265. SUBTRACT  ^sh1,length(sh1),^sh2,length(sh2)   ^diff,length(diff)
  266.  
  267. All arguments and result lists are listed in order of stacking.  Thus,
  268. ^ad1 is stacked before its length before ^ad2 before its length in the
  269. invocation of ADDER.  ADDER stacks ^sum before it stacks the length of
  270. the sum in its return of the result.  All lengths of numeric values are
  271. expressed in words (units of 16 bits); all lengths of decimal strings
  272. are expressed in bytes (characters).
  273.  
  274. ADDER
  275.  
  276. The sum from ADDER replaces the value of its first argument.  That is,
  277. the offset value returned from ADDER is either unchanged from that in
  278. its first argument or points a very few words lower in memory.  (Only
  279. the possibility of growth in length due to the addition makes it
  280. necessary to return the result pointers on the stack at all).  The second
  281. argument to ADDER is not changed by it, except that the guard word is
  282. zeroed.
  283.  
  284. SUBTRACT
  285.  
  286. The first argument to SUBTRACT must be larger than the second so that
  287. the result of the subtraction will be positive (remember:  this is a
  288. positive integer arithmetic package).  The difference (the second
  289. argument is subtracted from the first) replaces the first argument, and
  290. the returned (offset,length) pair reflects any length loss when the two
  291. arguments are close to one another in value.  The second argument to
  292. SUBTRACT is not changed, except that the guard word is zeroed.
  293.  
  294.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 6
  295.  
  296. The requirement that the first argument to subtract be larger than the
  297. second is sometimes awkward.  For example, in the calculation of
  298. v:=a*(u'-u)+v' in CFFFCOMB, see below, it was necessary to proceed as
  299. in v:=v'+a*u'-a*u.  The result v is always positive even though the
  300. difference u'-u is sometimes negative.
  301.  
  302. MULTIPLY
  303.  
  304. MULTIPLY returns its result in a special product register (MPIPR) which
  305. is double length.  MULTIPLY destroys neither of its arguments.  Since
  306. MULTIPLY clears MPIPR as it starts operation, neither of its arguments
  307. can be in MPIPR (i.e. neither can be the in situ result of a previous
  308. multiplication).  To do a chain of multiplications, you must move the
  309. earlier result into a work register and then use the work register as
  310. the argument to the next MULTIPLY invocation (as is done, for example,
  311. in COND2B (at the assembler level) and in DIVTEST (at the Pascal
  312. level, see below)).
  313.  
  314. The MULTIPLY routine requires that its arguments be correctly
  315. normalized, i.e. that the pointers to its arguments point to a single
  316. guard word preceding the significant words of the values.  This
  317. requirement arose from a desire to minimize overhead in probablistic
  318. primality testing; MULTIPLY takes a zero value in word[1] of an operand
  319. as a true zero without checking the length field, and immediately
  320. returns a zero result if word[1] of either operand is zero.
  321.  
  322. DIVIDE
  323.  
  324. When DIVIDE operates, the quotient is returned as the first returned
  325. value ((offset,length) pair) in a special quotient register (MPIQO) and
  326. the remainder is returned as the second returned value ((offset,length)
  327. pair) in the register the dividend was in.  Thus, DIVIDE destroys its
  328. first argument (the dividend) by leaving the remainder in its place.
  329. The second argument (the divisor) is net unchanged by DIVIDE, but the
  330. divisor has sometimes undergone normalization and unnormalization
  331. (Knuth's words, see his division algorithm) by two left shifts totalling
  332. 16 bits, followed by a right shift of one word.  (The right shift of one
  333. word is done to avoid the possibility that a divisor might be shifted
  334. out of its register by repeated consecutive use.)  Since the guard word
  335. has been filled and a second guard word sometimes appropriated in such
  336. normalization activity, two guard words should be available in front
  337. of a value which is to be used as divisor in a DIVIDE invocation.  This
  338. observation is especially cogent if a value in a constant table is used
  339. as a divisor by DIVIDE.
  340.  
  341. In the event of division by zero, DIVIDE returns the highest possible
  342. integer in MPIQO.  This result is referenced in the result list by both
  343. the quotient offset and the remainder offset.  This value is a string
  344. of X'FFFF values of length RLEN (32) words (even the guard word has
  345. X'FFFF in it).  Thus, if the user is concerned with the possibility of
  346. division by zero, he should test the guard word in the quotient or
  347. remainder and discover the X'FFFF in the event of a zero divide having
  348. occurred.  In the event of division into zero, the quotient and
  349. remainder are both zero (both returned offsets reference the
  350. zero-valued dividend).  If the dividend is smaller than the divisor, the
  351. quotient (zero) is created in MPIQO and the dividend is returned as the
  352. remainder.
  353.  
  354.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 7
  355.  
  356. A chain of divisions (as in CONB2D, where a series of divisions by 10
  357. are performed) cannot be performed in situ (because when the quotient in
  358. the MPIQO register from the previous DIVIDE is passed as dividend to a
  359. subsequent DIVIDE, new quotient words overwrite the current dividend
  360. words, still in use, and chaos ensues).  It is necessary to move the
  361. contents of the quotient register into a work register in order to
  362. perform a subsequent division in a chain of divisions.
  363.  
  364. Although a chain of either multiply or divide operations are not
  365. possible without moving an earlier result into a work register, it is
  366. possible to do a multiply followed by a divide without moving any
  367. intermediate results to a work register.  Since probabilistic primality
  368. testing involves raising a large number to a large power modulus another
  369. large number, which involves repeatedly multiplying and then dividing to
  370. determine the remainder, this capability was a design feature required
  371. for efficiency.  This process uses the product from a MULTIPLY (in
  372. MPIPR) as the dividend in the DIVIDE.  The remainder stays in the MPIPR
  373. register, from which it must be removed before the next MULTIPLY.
  374.  
  375. SHIFTLEFT
  376.  
  377. The register whose value is to be left shifted is indicated as usual as
  378. an (offset,length) pair in the first and second stacked arguments.
  379. SHIFTLEFT's stacked third argument is that power of 2 which results in
  380. the correct amount of left shifting when it is multiplied into
  381. SHIFTLEFT's other argument.  (I.e., to left shift n bits, use a third
  382. argument whose value is 2**n.)  The shifted value is returned in the
  383. same register that it came in; only the possibility that the length and
  384. starting position may have changed through overflow requires that the
  385. result descriptor be returned on the stack.  Zero bits come in from the
  386. right.  (Note to Turbo Pascal users:  the MPIFACE routine accepts a
  387. pointer to a word containing 2**n and moves the value 2**n to the stack
  388. before calling SHIFTLEFT.)
  389.  
  390. Note that right shifting by n bits can be achieved by left shifting 16-n
  391. bits followed by a length decrement of 1 word.  (The length decrement
  392. effects the discard of the bits which a right shift would have shifted
  393. off the edge of the rightmost word.  If the SHIFTLEFT routine detects
  394. overflow, it automatically appropriates a new guard word and increments
  395. the length value.)  Doing repeated right shifts by this device to a
  396. value in a register is not advisable because it will eventually result
  397. in the value drifting leftwards out of the register.  The fact that a
  398. right shift can be achieved by a complementary left shift implies that
  399. the omission of a right shift routine is not a genuine limitation.  An
  400. example of the use of LEFTSHIFT for the purpose of doing a right shift
  401. appears in the Pascal routine, LUCLEHMR, q.v. below.
  402.  
  403. The LEFTSHIFT routine is employed in the division routine to normalize
  404. and unnormalize the divisor, dividend, and remainder.  The LEFTSHIFT
  405. routine achieves its end by a single left to right pass across its
  406. operand in a 7-command loop (compare the 13-command innermost loop in
  407. multiply).  Hence, multiplication and division by a power of 2 is more
  408. rapidly achieved by left or right shifts than by multiplication or
  409. division.
  410.  
  411.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 8
  412.  
  413. PROBPRIM
  414.  
  415. The PROBPRIM routine accepts an odd multiple precision integer as
  416. argument in the usual (offset,length) pair form and returns either a 'P'
  417. or a 'C' in register AL, according as the number passed it is probably
  418. Prime or Composite.  This method of returning a result is unique in this
  419. package.  PROBPRIM does not destroy its argument, but the value has
  420. often been manipulated in situ by PROBPRIM.  Since PROBPRIM works by
  421. doing a long sequence of multiplications and divisions, the number whose
  422. primality is to be tested must not reside in either the MPIPR or the
  423. MPIQO register, but must instead (if it was produced in either of these)
  424. be moved to a work register whence it may be passed as an argument.  The
  425. argument for PROBPRIM must be odd -- it does not give reliable results
  426. for even arguments, which are, after all, always composite.  (2 is not a
  427. large enough number to be an interesting prime.)
  428.  
  429. COND2B
  430.  
  431. The decimal string which is passed to COND2B for conversion to internal
  432. format must be terminated with a blank or a control character (any
  433. character whose value is less than or equal to X'20).  This arises
  434. effortlessly (in assembler) if the input values are read into a buffer
  435. from a blank-delimited text file with carriage-return and/or line-feed
  436. as the record delimiters.  A pointer to the leftmost position of the
  437. numeric string in the buffer can be passed to COND2B and either the
  438. internal blank in the record or the trailing record terminator then
  439. serves as the closing delimiter of the decimal string for COND2B.  (Note
  440. to Pascal programmers:  Turbo Pascal does not include the record
  441. delmiters in the string value it reads, so you must append a blank to a
  442. decimal string before passing it to COND2B.)  Leading blanks and
  443. internal commas are tolerated (ignored) in the decimal string passed as
  444. an argument to COND2B.  The second argument is a pointer to the left
  445. edge of a register in which the equivalent numeric value is to be
  446. returned.  COND2B assumes the register is RLEN (32, in the usual case)
  447. words long and returns the binary (hex) value as the result, right
  448. justified in the register, pointed to by the usual (offset,length) pair
  449. on the stack.
  450.  
  451. CONB2D
  452.  
  453. CONB2D converts an internal multiple precision number into a decimal
  454. character string.  The first argument points to the rightmost position
  455. for the returned decimal character string; that byte is filled with X'00
  456. and the positions to its left are filled with the successively more
  457. significant decimal digits which represent the value in the register
  458. specified in the (offset,length) pair of the third and fourth arguments
  459. to CONB2D.  The second argument (the length component of the first
  460. (offset,length) pair) is just a dummy; any value will serve.  The result
  461. is returned as the usual (offset,length) pair.  The offset points to the
  462. first character of a decimal string delimited in a way which will allow
  463. it to serve in a subsequent invocation of COND2B.  The length value
  464. returned from CONB2D is the string length (in bytes) of the decimal string
  465. returned, including the terminal delimiter.
  466.  
  467.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 9
  468.  
  469. CONSTANT VALUES AS ARGUMENTS
  470.  
  471. Since many MPI routines zero the leading words of their arguments, some
  472. caution must be observed in using values in constant tables as arguments
  473. to routines, lest the values of constants in the tables to the left of
  474. those used as arguments be incorrectly zeroed as side-effects of the
  475. subroutines.  This is especially important in constants used as divisors
  476. where the normalization and un-normalization of Knuth's division
  477. algorithm can cause the use of two guard bytes.  The cautious approach
  478. is to move a value from the constant table into a work register and then
  479. use the work register as the argument to a routine.  The more efficient
  480. approach is simply to provide an adequate number of leading words in the
  481. constant tables to avoid any bad side effect of this zeroing and
  482. normalizing activity.  This approach was used for the powers of 2 in
  483. DIVTEST.PAS, for example.
  484.  
  485.  
  486. --Discussion of probabilistic primality testing
  487.  
  488. It is the nature of the probabilistic primality test to determine
  489. absolutely if a number is composite, but only to determine with odds of
  490. 3:1 that a number is prime.  Therefore, if a number is ever found to be
  491. composite, that result may be depended upon.  If a number is said to be
  492. prime (with only 1 chance in 4 that it isn't so), the action to take is
  493. to resubmit it to PROBPRIM repeatedly to get a number of independent
  494. assessments.  If, in two successive determinations PROBPRIM says 'P',
  495. the odds are only 1 in 16 that it is really composite; if three, only 1
  496. in 64; if four, only 1 in 256; and so on.  By doing a large number, m,
  497. of successive determinations, if all results are 'P', one can be sure of
  498. the primality of the argument to a vanishingly small probability (1 in
  499. 4**m) of being incorrect.  (The 1 in 4**m chance of error is only what
  500. is provably true.  Actually the 'P' determination is likely much more
  501. probably correct than that.  One very rarely observes PROBPRIM "changing
  502. its mind" after an initial 'P' is returned.  The example routine
  503. included here (PPTEST.8) does only a single primality test for each
  504. element of a 15-member set of closely spaced numbers, regardless of
  505. whether the first determination of each element results in 'P' or 'C'.)
  506.  
  507. PROBPRIM maintains a random number (2 words long) which it employs as a
  508. fraction by which to multiply the primality candidate (N) to calculate
  509. the number (X<N) which is to be raised to the N - 1 power in the
  510. probabilistic primality algorithm.  The next number in the pseudo-random
  511. sequence is calculated every time PROBPRIM is invoked.  This
  512. pseudo-random number sequence starts with a constant seed (1223312233,
  513. X'48EA4369) so it traverses the same sequence on every use of the
  514. package.  If you intend to make an independent primality determination
  515. by rerunning a sequence of invocations, be sure to change things so that
  516. each primality test becomes associated with a different pseudo-random
  517. number on each of the independent runs you make.  This can easily be
  518. done by cyclically permuting the records in the file of test cases.
  519.  
  520. Several features were built into the code of PROBPRIM and the supporting
  521. routines to increase the efficiency of the primality testing.  The
  522. requirement that its argument be odd allows the PROBPRIM code to start
  523. right off with the assumption that the rightmost bit is lit and thus
  524. avoid one cycle of the major loop in that routine.  (Even numbers except
  525. for 2 are not prime -- don't even test them.)  This is the first
  526. efficiency feature used here.
  527.  
  528.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 10
  529.  
  530. The raising of a randomly selected large number (X<N) to a large power
  531. (N-1) in the probabilistic primality test entails repeatedly squaring it
  532. (X**P, P=1,2,4,8,16,... in MPIXP) once for each bit in the binary
  533. representation of the power (N-1) and then multiplying that result into
  534. an accumulator (Y in MPIY) if the given bit is lit.  (All this is done
  535. modulo the primality candidate (N in MPIN), which means a division
  536. follows each multiplication.)  Since this amounts to much labor in
  537. processing each bit it is important to stop as soon as all significant
  538. bits are processed.  A special test is incorporated into the PROBPRIM
  539. code to detect when the most significant word is in progress and then to
  540. test when the most significant bit is finished so that the routine can
  541. stop its major loop promptly at that time.  This is the second
  542. efficiency feature used.
  543.  
  544. The division routine often determines that the first quotient digit is
  545. zero in the long division process.  When the quotient digit is zero,
  546. there is little point in going through the lengthy multiplication of the
  547. divisor by zero and subtraction of the (zero) product words from the
  548. dividend words.  Accordingly, the division routine skips around the
  549. multiply and subtract loop when the quotient digit is zero.  This is a
  550. third efficiency feature in the code.
  551.  
  552.  
  553. --Detailed Technical Notes
  554.  
  555. The .COM and .OBJ files supplied here were produced by the following
  556. commands:
  557.  
  558.   A86 MULTP.8 TO MULTP.OBJ                           (RLEN=32)
  559.   A86 MULTP.8 PROBP.8 TO PROBP.OBJ                   (RLEN=32)
  560.   A86 PPTEST.8 MULTP.8 PROBP.8 TO PPTEST.COM         (RLEN=32)
  561.   A86 MPIFACE.8S MULTP.8S PROBP.8S TO MPIFACE.OBJ    (RLEN=32 version)
  562.   A86 MPIFACE.8L MULTP.8S PROBP.8S TO MPIFACEL.OBJ   (RLEN=630 version)
  563.  
  564. If reassembly is required, the above commands can be employed again to make
  565. revised versions.  The size of MPIFACE as assembled above is under 5K
  566. so the space requirements for its use are minimal.  MPIFACE and MPIFACEL
  567. are the interface routines which can be called from Turbo Pascal to
  568. interface to the Multiple Precision Integer arithmetic routines.  MPIFACE
  569. is the version with ordinary (32 word) precision, and MPIFACEL has
  570. extraordinary (630 word) precision.
  571.  
  572. The MULTP routines include the basic operations without probabilistic
  573. primality testing.  PROBP contains the probabilistic primality testing
  574. routine (which calls the basic ones, so it is assembled with them.)
  575.  
  576. If the user wants to use only the basic routines without the
  577. probabilistic primality testing, he links MULTP.OBJ into his other
  578. routines.  If he wants to invoke PROBPRIM as well as the basic routines,
  579. he links PROBP.OBJ into his other routines.  The PPTEST.COM is for
  580. demonstration purposes, to illustrate how the routines might be employed
  581. from the assembler level.  To include the MPI routines in your own A86
  582. code, just substitute your source code name for 'PPTEST.8' in the third
  583. line above to produce a <yourfn>.COM file which includes the MPI
  584. support.
  585.  
  586.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 11
  587.  
  588. To use the routines from Turbo Pascal 4, make MPIFACE (or MPIFACEL)
  589. available to the Turbo compiler by using the compiler directive {$L
  590. mpiface} (or {$L mpifacel}) and invoking the interface routines as
  591. demonstrated in the TURBTEST.PAS, DIVTEST.PAS, P50TO128.PAS,
  592. MERSENNE.PAS, LUCLEHMR.PAS, and CFFFCOMB.PAS programs.
  593.  
  594.  
  595. --Description of the test and timing results at the assembler level
  596.  
  597. The PPTEST routine and the files SAVVY.SAM and ANSWER.SAM are provided
  598. to give a working example, its input, and the expected results.  This
  599. example operates entirely at the A86 language level, without Pascal.
  600. The decimal number in each record of SAVVY.SAM (the input file) is a
  601. homologue of 67.  The PPTEST program tests the homologues of each of the
  602. fifteen primes from 11 ... 67 for primality and produces a 'PC' string
  603. indicating which members are probably prime and which are composite.
  604. The individual primality candidates are obtained by subtracting from the
  605. input number {56, 54, 50, 48, ... 8, 6, 0} (these are the differences
  606. between 67 and each of the fifteen primes in the 11 ... 67 range) in
  607. turn to determine the values of the homologues of those primes.  Thus,
  608. the test file provides 300 examples (20*15) of the use of the PROBPRIM
  609. routine with 28 and 29 digit numbers.  The demo set is highly selected
  610. to contain cases which give relatively frequent primes --
  611. run-of-the-orchard homologues of 67 are far less likely to contain
  612. primes than this sample set.  For one thing, none of the 15 members of
  613. each of the homologous sets has a divisor less than 50,000; even among
  614. homologous sets relatively prime through 50,000, this is a selected
  615. subset particularly rich in primes.
  616.  
  617. The following timings are all run on the example SAVVY.SAM herein.
  618.  
  619. PPTEST runs in about 280 seconds on an 8086 machine running at 8MHz.
  620. PPTEST runs in about 59 seconds on an 80286 machine running at 12MHz.
  621. PPTEST runs in about 26 seconds on an 80386 machine running at 20MHz.
  622.  
  623. It took a SAVVY program 15,100 seconds (yes, 4.2 hrs) to do this work on
  624. the same 8086 machine at 8MHz.  That represents a speed improvement of a
  625. factor of 54 in the code and an overall performance improvement, code
  626. and machine, of a factor of 580!
  627.  
  628.  
  629. --Use of the Turbo Pascal interface routine, MPIFACE
  630.  
  631. The MPIFACE routine exists for the sole purpose of allowing access to
  632. the MPI routines from a high level language.  If you are working solely
  633. at the assembler level, you will not need to employ MPIFACE at all.  As
  634. mentioned above, it isn't necessarily easy to use MPI even from Pascal,
  635. but at least the mechanism is there and the examples should be consulted
  636. for details and ideas about how to get various jobs done.  The examples
  637. contain some variety of viewpoint and sophistication.
  638.  
  639.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 12
  640.  
  641. Since the user must define registers to contain his own values and must
  642. invoke the routines of the multiple precision package in the appropriate
  643. sequence to accomplish his ends, the general feeling of a Pascal program
  644. which does multiple precision arithmetic is that of an assembler
  645. language program.  Thus, the user programs at an operation by operation
  646. level, is concerned with register assignments, with the possible loss of
  647. values in registers through side effects of a called routine, with
  648. maintenance of values across routine calls where values are destroyed by
  649. a routine's action, and (sometimes) with the "hardware" details
  650. associated with production or maintenance of values right justified in
  651. the integer registers.
  652.  
  653. The basic mechanism for passing arguments from Pascal to -- and
  654. receiving values back from -- the multiple precision routines via the
  655. MPIFACE interface routine is to employ a control record, whose fields
  656. are filled with the values describing the operation to perform and its
  657. operands, and in whose fields are found the descriptors of the returned
  658. results.
  659.  
  660. The declaration of this control record appears in the *.PAS examples.
  661. The details of this record need not be of concern because it is easily
  662. filled by use of the MAKECONT routine, below, whose arguments provide
  663. the values to be placed in the control record.  Suffice it to say that
  664. the control record contains the operation to be performed, two pointer
  665. values and two length values, which together suffice to specify the
  666. arguments and results for any of the MPI routines.
  667.  
  668. The MPIFACE module moves/converts the information in the control record
  669. into the stacked argument list used by the multiple precision package,
  670. makes the indicated call, and then moves/converts descriptors of the
  671. returned values from the stack into the control structure.  Only the
  672. operand fields required for the specified routine are referenced, and
  673. only those required to describe the value returned by the invoked
  674. routine are replaced upon return, but dummy pointer values are often
  675. arbitrarily changed in the control structure (even though not used)
  676. because a global approach was taken to the problem of pointer to offset
  677. interconversion.
  678.  
  679. The following table summarizes the effect of each routine on the
  680. contents of the registers/strings employed as arguments.  An entry 'r'
  681. in column 3 indicates a returned value replaces the argument; an entry
  682. 'd' in column 3 indicates an argument is destroyed; an entry 's' in
  683. column 3 indicates an argument survives the routine's action without
  684. change; an additional entry 'm' indicates that the argument has been
  685. modified during the routine but restored so that it survived unchanged
  686. overall; an additional entry 'g' indicates that the guard word is actively
  687. zeroed or that the argument is (sometimes) padded on the left with one
  688. or two zero guard words as a side effect of the action of the routine.
  689. The two conversion routines return a length word in addition to the
  690. location word among the arguments.
  691.  
  692.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 13
  693.  
  694.     SUMMARY OF ARGUMENTS AND SIDE EFFECTS FOR PASCAL PROGRAMMERS
  695.  
  696.                                             effect on   other    arg
  697. action    arguments in control record       argument    return   must
  698. ABBRev'n  ('?' means dummy)                 values      medium   avoid
  699. ________  ________________________________  _________   ________ _____
  700.  
  701. ADDEr     ^ad1,^ad2,length(ad1),length(ad2) rg,sg,r,s
  702. CONB2d    ^right(decstr),^op,?,length(op)   r,d,-,r              MPIPR
  703. COND2b    ^left(register),^left(decstr),?,? r,s,r,-
  704. DIVIde    ^dvd,^dsr,length(dvd),length(dsr) r,smg,r,smg (MPIQO)  MPIQO
  705. MULTiply  ^mp1,^mp2,length(mp1),length(mp2) s,s,s,s     (MPIPR)  MPIPR
  706. PROBprim  ^op,?,length(op),?                sm,-,sm,-   (AL)     MPIPR,MPIQO
  707. SHIFtleft ^op1,^2**n,length(op1),?          rg,s,rg,-
  708. SUBTract  ^sh1,^sh2,length(sh1),length(sh2) rg,sg,r,s
  709.  
  710. The carets (^) in this table represent pointers, not offsets (unlike the
  711. previous similar table).
  712.  
  713. In the view of the MPI routines, all variables are addressable from the
  714. CS register value (.COM routine style).  Pascal is using various
  715. registers for addressing data, in various situations.  The interface
  716. makes appropriate offset changes coming and going to accommodate these
  717. differing views to one another.  (In brief summary, MPIFACE keeps the
  718. offset differences and corrects the offsets back to Pascal's view before
  719. return.)  To allow this accommodation of register usages, registers
  720. passed to the MPI routines must all occur within a single 64K segment so
  721. that the largest offset from the CS register will never exceed X'FFFF.
  722. That can limit the total size of the invoking Pascal routine, the
  723. interface, and the multiple precision routines.  CFFFCOMB, the largest
  724. program yet attempted, had to be carefully tuned to achieve a MAXM value
  725. of 114 by making all the registers global, and collecting all variables
  726. to be passed to MPIFACE together at the front of the main routine.
  727.  
  728. When a value from a register must be kept for future use because it is
  729. about to be destroyed as a side effect of some operation, both the value
  730. in the register and its (pointer,length) descriptor must be kept.  There
  731. are two basic ways of handling this.  One way is to copy the value and
  732. keep its descriptor and later move the value back into its original
  733. location before use.  In this case, the descriptor can be used just as
  734. it was.  The other way is to use the value in the register it was moved
  735. into.  In this case, the values in the descriptor must be reassigned so
  736. that it represents the position of the value in the new register, not
  737. the position in the original register from which it was saved.  The
  738. first method involves two moves, but no descriptor change.  The second
  739. method involves one move and one descriptor change.  There is a compact
  740. example of the second method among the examples at the end of this
  741. document.
  742.  
  743. Moving a value can itself be done in either of two ways:  either the
  744. whole register can be moved, regardless of the size of the portion of
  745. it that is filled, or just the filled portion can be moved.  These
  746. options appear (and are commented) among the examples of PASCAL code at
  747. the end of this document.
  748.  
  749. These complications are illustrated in the various *.PAS example
  750. programs herein.  A few key cases are excerpted near the end of this
  751. document.  By studying the examples, it will be possible to gradually
  752. develop a full understanding of all these details.
  753.  
  754.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 14
  755.  
  756. Since the PROBPRIM routine returns its result by a unique mechanism, it
  757. is invoked by PPTFACE(<ctrlptr>) and the result is assigned into a
  758. character.  The usual invocation uses MPIFACE(<ctrlptr>) and the result
  759. is assigned into a ctrlptr.  The entry points are synonymous and, except
  760. for this name change, which serves to fool the Pascal typing
  761. requirements, preparation for invocation of the primality test is
  762. identical with that of the other routines.
  763.  
  764. To bring its parameter-passing convention into conformity with the
  765. practice in the rest of the package, a pointer to a power of two is
  766. employed as argument in calling the routine SHIFTLEFT.  The interface
  767. accepts the pointer and uses it to get the value of 2**n which it stacks
  768. to pass to the routine.
  769.  
  770.  
  771. --Description of tests and timing results
  772.  
  773. A number of test programs (demonstrations) using Turbo Pascal programs
  774. as drivers of the multiple precision subroutines are included.  These
  775. programs did yeoman duty in revealing bugs and suggesting revisions in
  776. the interface and in the MPI routines to improve their overall accuracy
  777. and utility.  The programs in this category are TURBTEST, DIVTEST,
  778. P50TO128, MERSENNE, LUCLEHMR and CFFFCOMB.
  779.  
  780. While doing these programs was exhausting, it is probably not true that
  781. they provided an exhaustive test of the routines, and users are
  782. cautioned to test their use thoroughly in their own applications before
  783. completely trusting them.  I would be grateful to hear of any bugs which
  784. you may detect.
  785.  
  786. TURBTEST just raises 111111111 to the fourth power by successive
  787. squarings, tests the result for primality (guess what?--it is
  788. composite), and outputs the value of the fourth power and the discovery
  789. that it is composite.  The program illustrates (very basic, primitive)
  790. techniques for copying the values of registers which are about to be
  791. destroyed as a side effect of the MPI routine to be called and provides
  792. examples of control structure and pointer manipulation in this
  793. connection.  The elements of the control structure are individually
  794. manipulated in inline code to establish the correct environment for an
  795. MPI call in this example.  Note the Pascal declarations of the MPI
  796. interface routine under two pseudonyms and the {$I+ directive} which
  797. includes the interface code and MPI routines.  TURBTEST runs so rapidly
  798. that timing is hardly a question.
  799.  
  800. DIVTEST is an implementation of a single example of the division
  801. algorithm test suggested by Knuth (p.261).  It consists of forming
  802. the product of 2**48-1 and 2**80-1 and then dividing that product by
  803. one of its two factors.  The form of the product puts the division
  804. routine through its less frequently utilized branches.  The fact that
  805. the other factor is produced as the quotient verifies the correct
  806. operation of the division routine in this probing instance.
  807.  
  808. Note that the code in DIVTEST is considerably simplified by the fact
  809. that the lengths of the values are throughout handled in the
  810. programmer's head, rather than by saving and using length values
  811. generated during the running of the program.  It will be rare programs
  812. indeed in which this degree of predictability will permit this stratagem
  813. to be employed.  DIVTEST runs so rapidly that timing information is
  814. irrelevant.
  815.  
  816.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 15
  817.  
  818. DIVTEST contains two internal procedures which may help to simplify
  819. (they certainly shorten) the Pascal programs which invoke MPI.  The
  820. procedure MAKECONT fills in the fields of the control record preparatory
  821. to an invocation of MPI through the interface.  MAKECONT takes an
  822. argument list of six items, the pointer to the control record to use,
  823. the pointers to the two argument values, the lengths of the two
  824. arguments, and the 4 character operation to be performed, and
  825. distributes them properly in the indicated control record.  By using
  826. this procedure a great deal of the diffuseness of the code evident in
  827. TURBTEST is avoided, and the source code is much tighter (and more
  828. readable?).  If an operation does not require a full panoply of arguments,
  829. the slots in the argument list of MAKECONT are just filled in with
  830. dummies.  These have been denoted with '?' in the above table.  The use
  831. of MAKECONT does not afford opportunity to achieve (admittedly very
  832. minor) efficiencies by reuse of values already in control structures
  833. without respecification, since MAKECONT requires all its arguments and
  834. fills them in regardless of their prior presence or status as a dummy.
  835.  
  836. The procedure MOVEIT moves a value from an MPI register into a
  837. user-defined register when it is required to retain a value about to be
  838. destroyed as an MPI side-effect.  It can also be used, of course, to do
  839. a simple value-transferring assignment.  MOVEIT moves only the guard
  840. word and significant words of the value in a register.  MOVEIT takes
  841. three arguments, the pointer to the (zeroth word of the) register the
  842. value is to be moved into, the pointer to the value to be moved, and the
  843. length of the value to be moved.  MOVEIT proceeds to move the value as
  844. requested.  Retention and/or revision of pointer information, which
  845. usually is required whenever MOVEIT is used, must be done independently
  846. of MOVEIT.  MOVEIT assumes that REGMAX defines all register lengths.
  847. Its value will be either 31 or 629, according as MPIFACE or MPIFACEL is
  848. in use.  Note that REGMAX is one less than RLEN, because registers in
  849. PASCAL have subscripts [0..REGMAX], while registers in A86 are RLEN
  850. words long, with zero-origin subscripting understood.
  851.  
  852. Here is the code for the two internal procedures which reduce the volume
  853. of PASCAL code in programs using MPI;  MAKECONT makes a control record;
  854. MOVEIT moves a value from one MPI register to another.
  855.  
  856. procedure makecont(cptr:ctrlptr; a1,a2:regptrtyp; l1,l2:word; op:c4);
  857. {arg 1 points to ctrl structure, 2&3 point to values, 4&5 give the
  858.    lengths of the values, and 6 the operation to be performed}
  859. {this routine simply stuffs the control structure specified in arg 1}
  860.    begin
  861.      cptr^.ptr1 := a1;
  862.      cptr^.ptr2 := a2;
  863.      cptr^.ln1  := l1;
  864.      cptr^.ln2  := l2;
  865.      cptr^.operation := op;
  866.    end;
  867.  
  868.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 16
  869.  
  870.  procedure moveit(into,from:regptrtyp; l:word);
  871. {arg 1 points to the into register at its zeroth word, arg 2 points to
  872.    the value in the from register at its guard word, arg 3 gives the
  873.    length of the value in the from register}
  874. {this routine moves a value in a register to another location}
  875. {control information about the value must be handled separately}
  876.    var i:byte;
  877.    begin
  878.      for i := 0 to l do
  879.      into^[regmax-l+i] := from^[i];
  880.    end;
  881.  
  882. The program P50TO128 merely identifies the prime numbers, as determined
  883. from three probabilistic primality tests, between 50,000 and 128K.
  884. Primes in other numeric ranges could easily be produced by simple
  885. modification of this routine.  The program is quite unexceptional, and
  886. provides a simple example of a Pascal program which employs the MPI
  887. routines.  It runs in about 42 minutes on the 8086 machine at 8MHz.
  888.  
  889. The program MERSENNE looks at the Mersenne numbers, 2**n - 1, where n is
  890. prime, and determines if they are prime or not by use of PROBPRIM.
  891. MERSENNE runs through the odd integers from 3 to nearly 500, determining
  892. whether each odd integer is prime or composite and, for those found to
  893. be prime, calculating 2**n - 1 and determining whether that (Mersenne
  894. number) is prime or composite.  An appropriate output line is produced
  895. for each odd integer.  The Mersenne numbers are output in decimal
  896. notation, thus exercising CONDB2D.  The program shuts down when it
  897. determines that the Mersenne number would be too large to fit in the
  898. default register size of 32 words (about 149 decimal digits).  The
  899. MERSENNE program could be revised with increased values of RLEN in the
  900. A86 routines and REGMAX in the Pascal routine, but this has not been
  901. done, since the run times would become excessive and the probabilistic
  902. primality test is not the test customarily employed to determine
  903. primality of Mersenne numbers.
  904.  
  905. The MERSENNE program provides an example of direct generation of
  906. multiple precision integers in a register at the Pascal level by taking
  907. advantage of the fact that 2**n is just a one bit followed by n zero
  908. bits.  Subtraction of 1 then produces the Mersenne number.  The program
  909. employs three probabilistic primality tests for each value of n to
  910. achieve odds of 64:1 that n is really prime, but only a single primality
  911. test is used for each 2**n -1.  This combination gets the right answers,
  912. but a few values of n between 3 and 500 give incorrect answers in the
  913. absence of the triple application of the probabilistic primality test.
  914.  
  915. MERSENNE runs in 42 minutes and 10 seconds on an 8086 machine at 8mHz.
  916.                  37             42  (89.40%)
  917. MERSENNE runs in 9 minutes and 48 seconds on an 80286 machine at 12mHz.
  918.                  7             39   (78.06%)
  919. MERSENNE runs in 4 minutes and 35 seconds on an 80386 machine at 20mHz.
  920.                  3             24   (74.18%)
  921.  
  922. The second set of numbers were obtained after a program improvement
  923. which consisted of suppressing the unnecessary zeroing of leading words
  924. of the shorter of the two arguments to ADDER and SUBTRACT.
  925. Interestingly, the faster machine benefitted to a greater degree from
  926. this change than the slower ones.
  927.  
  928.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 17
  929.  
  930. The LUCLEHMR program implements the Lucas-Lehmer test of primality of
  931. Mersenne numbers.  (See Knuth, ob cit, Theorem L, p 391.)  This program
  932. has been created using the high precision version of MPI, MPIFACEL.OBJ.
  933. The run time would be excessive if the whole range of numbers from 3 to
  934. 10000+ were processed so the program has been arranged to skip over long
  935. sequences of values of n which are known not to give prime Mersenne
  936. numbers.  (The run time is still excessive, and no one will wish to run
  937. this entire program.)  This program starts like MERSENNE in determining
  938. which odd numbers are primes, but then determines the primality of 2**n
  939. - 1 and outputs the value of each prime n in an appropriate column in
  940. accord with the result of the Lucas-Lehmer procedure.  There is no
  941. production of the value of the Mersenne number either in the machine or
  942. for output.  The correct answers, i.e. those values of n for which 2**n
  943. - 1 is prime, are 2, 3, 5, 7, 13 ,17, 19, 31, 61, 89 ,107, 127, 521,
  944. 607, 1279, 2203, 2281, 3217, 4253, 9689, 9941, .... .
  945.  
  946. The notable feature of the code is the gyration to achieve the
  947. separation of L**2 - 2 into a least significant half of 2**q - 1 bits
  948. and a most significant half of the same length to add together (remember
  949. casting out nines?) to form (L**2 - 1) mod (2**q - 1).  This is
  950. complicated by the necessity to maintain correct argument lengths and
  951. offsets.  It may be high level language code, but that does not make it
  952. readable!
  953.  
  954. Tests of the effect on the running time of the alignment of the MPIPR
  955. register in memory were made using minor variations of the LUCLEHMR
  956. program, obtained by shifting some of the 'byte' variables into the list
  957. of 'word' variables at the PASCAL source code level.  (I was unable to
  958. discover a more rational way of obtaining program versions with specific
  959. memory alignments in the linked-in assembler portions.)  The D86
  960. debugger was then used to determine the alignment of MPIPR and timing
  961. runs made both with a byte-aligned variant and a word-aligned variant.
  962. It turned out that a performance penalty of 15.5% results from
  963. byte-aligning instead of word-aligning the MPIPR register, which plays
  964. such an active role in LUCLEHMR.
  965.  
  966. The CFFFCOMB (Continued Fraction Factor, Find COMBination) program
  967. implements Knuth's continued fratcion algorithm (ibid, p381, algorithm
  968. E) and his linear combination algorithm (ibid, answer to problem 12,
  969. p610).  It will usually result in discovery of a factoring.  The
  970. declaration of a large number of registers to hold the large set of
  971. variables in that algorithm and a nearly slavish following of the algorithm
  972. have led to a fairly straight-forward transcription of the algorithm at
  973. the Turbo Pascal level.  This routine seems the best to look at for an
  974. illusion of distance from the assembler level.
  975.  
  976. A 'friendly' interface to CFFFCOMB is provided in FACTOR.BAT.  Use it to
  977. get your feet wet or for casual applications.  For production factoring,
  978. you'll want to manipulate the input and output files for yourself, which
  979. will require learning the information in the following paragraphs.
  980.  
  981. To use CFFFCOMB to discover the factors of composite numbers in the
  982. general range of up to 29 decimal digits, put the numbers to be factored
  983. in NSIN.FIL, one to a line.  Use PRIMES.FIL as a second input file.
  984. CFFFCOMB selects a value of k to avoid the occasional recalcitrant case
  985. which arises if a particular value of k is used regularly.  {Because k
  986. is selected on the basis of ten useable primes, be sure to have enough
  987.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 18
  988.  
  989. primes in PRIMES.FIL so that at least ten primes get included.}  CFFFCOMB
  990. selects those primes which it can use in factoring each different
  991. composite and uses not more than 114 of them.  Some experimentation may
  992. be required to determine the optimal number of primes to load into
  993. PRIMES.FIL (from PRIMES29.FIL) for any given numeric range of problem,
  994. but the choice is not usually very critical.  CFFFCOMB puts its results
  995. into a file called OUTFILE.FIL.
  996.  
  997. When CFFFCOMB runs, the first screen output gives the list of primes
  998. which will be used in seeking a factor, the value of k used, and the
  999. number, m, of primes in the list.  Second and subsequent lines to the
  1000. screen give the value of x (in the form of a base 65536 number), the
  1001. string of exponents, e[i], which satisfy Knuth's equation (18), and a
  1002. parenthesized count of the number of such discoveries made and
  1003. accumulated.  The linear combination portion of the program watches for
  1004. a combination even in the e's which produces a factoring (or a 'glitch'
  1005. message if x=y or x+y=n for a particular combination).  A final line
  1006. gives the factors discovered, and the factorization is also output to
  1007. OUTFILE.FIL.  The factoring will be found before the number of
  1008. accumulated instances of satisfaction of (18) exceeds m+2.
  1009.  
  1010. The trade-off in use of CFFFCOMB is as follows.  The more primes that
  1011. are used as the basis, the more frequently equation (18) is satisfied.
  1012. But at the same time, more divisions will be required to find each
  1013. instance and more instances will be required to find an even linear
  1014. combination of the exponents.  If too few primes are used (for the size
  1015. of the number being factored), it will be a long time between
  1016. discoveries of instances of satisfaction of (18), and factoring will
  1017. take a very long time.  If outputs to the screen are extremely rapid,
  1018. you probably have more than the needed number of primes in PRIMES.FIL,
  1019. and cutting that number down may speed up the process.  The trade-off
  1020. does not seem to be very critical, and the process is usually marginally
  1021. faster if more primes are used.
  1022.  
  1023. CFFFCOMB is an example of a program whose capacity is restricted by a
  1024. 64K size limit.  The number of primes used in the continued fraction
  1025. stage is limited.  By rearranging the order of declaration of the
  1026. variables, it proved possible to make all variables which MPI must
  1027. access fall within its 64K addressing range, and an array size of 114
  1028. could be specified.  At this point, the variables (to which e, used in
  1029. accumulating the even linear combination of exponents, is the largest
  1030. contributor) are just short of exceeding the 64K size limitation of the
  1031. data segment of a Turbo Pascal 4 program.  This limits the size of
  1032. number which can be factored to something a little over 29 digits.
  1033.  
  1034. The exponents accumulated in seeking an even linear combination of
  1035. exponents sometimes exceeded the capacity of a longint (about 2E9),
  1036. overflow occured, and the value 1 was reported as a factor during early
  1037. trials.  When the linear combination algorithm was altered so that the
  1038. primes are scanned from highest to smallest, this behavior stopped.
  1039. Thus the maximum 114 primes can be used routinely, at the possible cost
  1040. of unecessary time expenditure.  If 1 is reported as a factor due to
  1041. this overflow, reduce the number of primes supplied in PRIMES.FIL so
  1042. that fewer than 114 primes are in the basis, let the computer run
  1043. longer, and the resulting less severe accumulation of the exponents may
  1044. avoid the overflow.
  1045.  
  1046.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 19
  1047.  
  1048. Sometimes the continued fraction portion of the program cycles
  1049. prematurely.  This behavior seems to be associated with an unfortunate
  1050. choice of k. It can usually be avoided by moving the block of primes
  1051. from 3 through 31 from its normal place in PRIMES.FIL to the end of
  1052. PRIMES.FIL.  This alters the set of primes on whose basis k is selected,
  1053. thus alters k, a different continued fraction itinerary is followed, and
  1054. the premature cycling is avoided.  The same remedy is effective if so
  1055. many 'x=y' or 'x+y=n' glitches arise that the program quits without
  1056. finding a factor, presumably because the accumulation of the exponents
  1057. makes an independent and more felicitous start in the altered rerun.
  1058.  
  1059. CFFFCOMB performs factoring of numbers up to 29 digits in a truly
  1060. impressive manner.  It factored the 15 members of a pentadecet site, all
  1061. of whose members were composite and with no factor smaller than 50,000,
  1062. in an average of just under 20 minutes each on the 386 machine at 20
  1063. MHz.  This is the NSIN29 example.
  1064.  
  1065. --Example fragments illustrating features of Pascal programs using MPI
  1066.  
  1067. { these are the PASCAL declarations which support the A86 subroutines, and
  1068.   the compiler directive which includes the MPI package }
  1069.  {$L mpiface}
  1070.     constant regmax = 31;
  1071.     type
  1072.       c4 = packed array[1..4] of char;
  1073.       register = array[0..regmax] of word;
  1074.       ctrlptr = ^ctrl;
  1075.       regptrtyp = ^register;
  1076.       ctrl = record
  1077.         operation: c4;                          {routine name abbrev.}
  1078.         ptr1,ptr2: regptrtyp;                   {argument locations}
  1079.         ln1,ln2: word;                          {argument lengths}
  1080.       end;
  1081.     function mpiface(a: ctrlptr): ctrlptr;  external;
  1082.     function pptface(a: ctrlptr): char; external;
  1083.  
  1084. *******************************************************************************
  1085.  
  1086.  { here's an illustration of prefixing 'constant' values with zero
  1087.    values as guard words by interspersing the values with zeroes}
  1088.   var zone,one,ztwo3,two3,ztwo5,two5,ztwo15,two15 : word;
  1089.  
  1090.  {we easily achieve the leading zero-valued guard word requirement :}
  1091.         one := 1;  two3 := 8;  two5 := 32;  two15 := 32768;
  1092.         zone := 0;  ztwo3 := 0;  ztwo5 := 0;  ztwo15 := 0;
  1093.  
  1094. *******************************************************************************
  1095.  
  1096. { here's a fragment of a chain mutliply from DIVTEST; first establish the
  1097.   address that the control structure resides at; then fill the control
  1098.   structure to square 2**15; then invoke mpiface; then move the copy
  1099.   to an auxiliary register to get ready for a chain multiply }
  1100.  
  1101.         argsptr := addr(argsdescr);
  1102.  {note how we address the guardwords, not the values themselves}
  1103.         makecont(argsptr,addr(ztwo15),addr(ztwo15),1,1,'MULT');
  1104.         argsptr := mpiface(argsptr);
  1105.  {we have to make a copy of this product to chain multiply it}
  1106.         moveit(addr(two30),argsptr^.ptr1,argsptr^.ln1);
  1107.  {now we probably need a new descriptor for that returned, moved value}
  1108.         two30ln := argsptr^.ln1;  two30pt := addr(two30[regmax - two30ln)];
  1109.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 20
  1110.  
  1111. *******************************************************************************
  1112.  
  1113. { here's a piece from DIVTEST; it makes a copy of a register in store because
  1114.   if left in MPIPR it would be destroyed; it clears the output string; it
  1115.   creates the control structure in argsptr; it invokes mpiface to do CONB;
  1116.   and it finally outputs the decimal string }
  1117.  
  1118. {the whole register is moved though under half full}
  1119.         store := two48t80m;
  1120.         output := '                                        ';
  1121. {we provide the descriptor components for the value in store on the fly}
  1122.         makecont(argsptr,addr(output[40]),addr(store[regmax-8]),1,8,'CONB');
  1123.         argsptr := mpiface(argsptr);
  1124.         writeln('Current answer is: ',output);
  1125.  
  1126. *****************************************************************************
  1127.  
  1128. { fragment from MERSENNE which does three prob. prim. tests, setting
  1129.   result to 'C' if composite is returned as result at any time }
  1130. { z1 is a zero word stored right before pcan, the variable whose value
  1131.   is really to be tested }
  1132.  
  1133.  result:='P';
  1134.    for i:=1 to 3 do begin
  1135.      makecont(argsptr,addr(z1),addr(z1),1,1,'PROB');
  1136.      porc := pptface(argsptr);
  1137.        if porc = 'C' then result := 'C'
  1138.    end;
  1139.  
  1140. *****************************************************************************
  1141.  
  1142. { fragment from program using a vector of control structures; first we
  1143.   get a copy of a homologue of 67; then we divide; then see if the
  1144.   remainder from division is one of the magic 15 {56,54,...,8,6,0} in
  1145.   deadset, which indicates a composite among the pentadecet members; then
  1146.   if so we test the primality of the quotient, having moved it into a work
  1147.   register, to see whether this was a complete factoring of that
  1148.   pentadecet element or not }
  1149.  
  1150.    moveit(addr(work),candescr[i].ptr1,candescr[i].ln1);  {only filled words move}
  1151.    makecont(argsptr,addr(work[regmax-candescr[i].ln1]),primedescr.ptr1,
  1152.       candescr[i].ln1,primedescr.ln1,'DIVI');
  1153.    argsptr:=mpiface(argsptr);
  1154.     if (argsdescr.ln2 = 1) and (argsdescr.ptr2^[1]<=56) and
  1155.          (argsdescr.ptr2^[1] in deadset) then
  1156.       begin
  1157.        writeln(primein,' divides ',pelin[i],' R',argsdescr.ptr2^[1]:2);
  1158.        moveit(addr(work),argsdescr.ptr1,argsdescr.ln1);
  1159.        makecont(argsptr,addr(work[regmax-argsdescr.ln1]),argsdescr.ptr1,
  1160.             argsdescr.ln1,argsdescr.ln1,'PROB');
  1161.        porc:=pptface(argsptr);
  1162.       end;
  1163.  
  1164. *******************************************************************************
  1165.          MULTIPLE PRECISION INTEGER ARITHMETIC           Page 21
  1166.  
  1167.  
  1168.  { compact illustration of production and use of a copy of a value preparatory
  1169.    to division which otherwise would have destroyed the value }
  1170.  { instead of establishing a separate pointer and length for work,
  1171.    values are calculated on the fly }
  1172.  
  1173.       moveit(addr(work),candescr[i].ptr1,candescr[i].ln1);
  1174.       makecont(argsptr,addr(work[regmax-candescr[i].ln1]),primedescr.ptr1,
  1175.            candescr[i].ln1,primedescr.ln1,'DIVI');
  1176.  
  1177. Advertisement(s)
  1178.  
  1179. The source codes for several of the Pascal programs included here were
  1180. developed using my SPA:WN package.  SPA:WN is an acronym for Structured
  1181. Programming Automated:  Warnier Notation.  The advantage of SPA:WN is
  1182. that it allows design and debugging of a program (in any language, but
  1183. Pascal here) with automated presentation of the program state at any
  1184. time in the form of a Warnier diagram and automated production of target
  1185. language source code at any time.  The structured nature of a program
  1186. developed in this way permits relatively easy revisions, even when they
  1187. are deep-seated, and the constant availability of an up-to-date Warnier
  1188. diagram is invaluable during the detailed debugging process.  The *.WAR
  1189. files herein are the source and site of maintenance of the *.PAS and
  1190. *.EXE files with identical filenames.  The interested reader may enjoy
  1191. comparing the relatively non-procedural presentation of the code in
  1192. the *.WAR versions with the classical procedural versions in the *.PAS
  1193. files.  SPA:WN is available from various shareware distribution centers
  1194. or from me for a self-addressed, stamped mailer, and 5 1/4" diskette.
  1195.  
  1196. Any serious use of this package should occasion remission of a suitable
  1197. contribution to the Research Foundation, 146 Durland Hall, Kansas State
  1198. University, Manhattan, KS 66506.  $50.00 is a sugested amount.
  1199.  
  1200. The names Turbo Pascal, SAVVY, A86, and D86 are trade marks or are
  1201. copyrighted by their respective owners.
  1202.  
  1203.  
  1204. I would be grateful to hear of any bugs you may discover in use of these
  1205. routines.  All comments, corrections, suggestions may be sent to:
  1206.  
  1207. Kenneth Conrow
  1208. Manager, Academic User Services
  1209. Computing Center, Cardwell Hall
  1210. Kansas State University
  1211. Manhattan KS, 66506
  1212. Phone: (913) 532-6311
  1213. Bitmail: KCONROW AT KSUVM
  1214.